#include "StdAfx.h"
#include "InternalTimer.h"

using namespace Adaptive::Foundation;
using namespace Adaptive::Win32;

/*--------------------------------------------------------------------------------
 * Constructor / Dispose / Destructor Methods
 *------------------------------------------------------------------------------*/
#pragma region Constructor / Dispose / Destructor Methods
// Initializes a new instance of the class.
InternalTimer::InternalTimer()
{
	LARGE_INTEGER value;

	value.QuadPart = 0;
	_interval = DEFAULT_INTERVAL;
	_ticksPerSecond = 0;
	_autoReset = true;
	_execThread = false;
	_timeThread = nullptr;
	_ct = new CRITICAL_SECTION();
	::InitializeCriticalSection(_ct);
	QueryPerformanceFrequency(&value);
	_ticksPerSecond = value.QuadPart;

}
// Initializes a new instance of the class.
InternalTimer::InternalTimer(float interval)
{
	LARGE_INTEGER value;

	value.QuadPart = 0;
	_interval = interval;
	_ticksPerSecond = 0;
	_autoReset = true;
	_execThread = false;
	_timeThread = nullptr;
	_ct = new CRITICAL_SECTION();
	::InitializeCriticalSection(_ct);
	QueryPerformanceFrequency(&value);
	_ticksPerSecond = value.QuadPart;

}
// Initializes a new instance of the class.
InternalTimer::~InternalTimer()
{
	Close();
	::DeleteCriticalSection(_ct);
	delete(_ct);
	_ct = NULL;
	_timeThread = nullptr;
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Properties
 *------------------------------------------------------------------------------*/
#pragma region Public Properties
bool InternalTimer::AutoReset::get()
{
	return _autoReset;
}
void InternalTimer::AutoReset::set(bool value)
{
	_autoReset = value;
}
float InternalTimer::Interval::get()
{
	return _interval;
}
void InternalTimer::Interval::set( float value )
{
    if (value <= 0)
    {
        throw gcnew ArgumentException("Invalid timer interval specified.");
    }
    _interval = value;
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Protected Methods / Event Methods
 *------------------------------------------------------------------------------*/
#pragma region Protected Methods / Event Methods
void InternalTimer::OnElapsed(EventArgs^ e)
{
    try
    {
		Elapsed(this, e);
    }
    catch(...)
    {
    }
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Public Methods / Functions
void InternalTimer::Close()
{
    Stop();
}
void InternalTimer::Start()
{
	if (_timeThread == nullptr)
	{
		::EnterCriticalSection(_ct);
		_execThread = true;
		_timeThread = gcnew Thread(gcnew ThreadStart(this, &InternalTimer::RunThread));
		_timeThread->IsBackground = true;
		_timeThread->Priority = ThreadPriority::AboveNormal;
		_execThread = true;
		::LeaveCriticalSection(_ct);
		_timeThread->Start();
	}
}
void InternalTimer::Stop()
{	

    if (_timeThread != nullptr)
    {
		::EnterCriticalSection(_ct);
		_execThread = false;
		::LeaveCriticalSection(_ct);

		//Do the polling method if the event handle did not get created.
		if (_threadEndEvent == NULL)
		{
			while (_timeThread->IsAlive)
				Sleep(10);
		}
		else
		{
			::WaitForSingleObject(_threadEndEvent, 30000);
			::CloseHandle(_threadEndEvent);
			_threadEndEvent = NULL;
		}

        _timeThread = nullptr;
	}
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Private Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Private Methods / Functions

void InternalTimer::RunThread()
{
	LARGE_INTEGER stTime;
	LARGE_INTEGER etTime;
    __int64 startTime = 0;
    __int64 endTime = 0;
    __int64 measuredValue = 0;
   
	_threadEndEvent = ::CreateEventW(NULL, false, false, NULL);

    while (_execThread)
    {
        measuredValue = (__int64)(_interval * _ticksPerSecond);
        QueryPerformanceCounter(&stTime);
		startTime = stTime.QuadPart;
        endTime = startTime;

        while ((_execThread) && (endTime - startTime < measuredValue))
        {
            QueryPerformanceCounter(&etTime);
			endTime = etTime.QuadPart;
        }

        if (_execThread)
        {
			OnElapsed(EventArgs::Empty);
            Sleep(1);
        }
    }
	if (_threadEndEvent != NULL)
		::SetEvent(_threadEndEvent);
	
}
#pragma endregion

